home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 13 - 1997 (partial) / 13.02 Feb 97 / HappyFace, a CTB Game / Game.c < prev    next >
Encoding:
Text File  |  1996-10-17  |  12.4 KB  |  432 lines  |  [TEXT/CWIE]

  1. // Game.c -- Contains logic to control high-level game activity
  2.  
  3. // Necessary includes (uncomment or precompile)
  4. #include "Sound.h"
  5. #include "QDOffscreen.h"
  6. #include "Game.h"
  7. #include "CTB.h"
  8.  
  9. // Useful literals
  10. // Metrics
  11. #define kPieceWidth        43    // Width and height in pixels of a square
  12. #define kOffset            42    // From edge of one to edge of adjacent
  13. #define kTitleBarHeight    18    // Window's title bar height?
  14. #define kNumCols        8    // # of Vertical columns  (max = 2^3 = 8)
  15. #define kNumRows        8    // # of Horizontal rows (max = 2^3 = 8)
  16. #define kMargin            20    // Window distance from edges of gray rgn
  17.  
  18. // Square/face status
  19. #define kEmpty            0    // Identifies an empty square
  20. #define    kHealthy        1    // Square with healthy face
  21. #define kStruckLocally    2    // Square with face struck by local player
  22. #define kStruckByOpponent    3    // Square with face struck by opponent
  23.  
  24. // Constraints
  25. #define kDyingTime        60    // Number of ticks it takes a face to die
  26. #define kMaxMade        5    // # faces allowed made locally at a time
  27. #define kWinningScore    25    // Number of faces struck to win the game
  28.  
  29. // Game Actions
  30. #define    kActionMadeFace        1
  31. #define    kActionStruckFace    2
  32. #define kActionNewGame        3
  33.  
  34. // Resource IDs
  35. #define rFirstGraphic    128    // Resource ID of first graphic PICT
  36. #define rStrikeSound    128    // Resource ID of striking SND
  37. #define rGenAlert        129    // Resource ID of ALRT that shows scores
  38.  
  39. typedef struct {
  40.     short    occupant;    // kEmpty, kHealthy, kStruckLocally, kStruckByOpponent
  41.     long    age;        // TickCount() at status change 
  42.     Rect    boundsRect;    // Bounding rectangle for gSquare
  43.     Boolean    madeLocally;// Keep track of whether made locally
  44. } contents;
  45.  
  46. static WindowPtr    gGameWindow;    // Pointer to our game's window
  47. static PixMapHandle    gPixMapHand[4];    // PixMaps describing graphics
  48. static contents        gSquare[kNumCols][kNumRows]; // Array of squares
  49. static short        gMadeCount,     // #locally-made faces
  50.                     gLocalStruckCount,// #locally-struck faces
  51.                     gOpponentStruckCount;// #opponent-struck
  52. static Handle        gStrikeHand;    // Handle to Strike SND
  53. static SndChannelPtr gSndChannelPtr;// Ptr to Channel to play SND
  54. static Rect            gPicRect;        // "Normalized" PICT Rect
  55.  
  56. //**********************************************************
  57. //
  58. // PlayStrikeSound - Plays sound of striking a face
  59. //
  60. //**********************************************************
  61. static void PlayStrikeSound(void)
  62. {
  63.     if (gStrikeHand && gSndChannelPtr)
  64.         SndPlay(gSndChannelPtr, (SndListHandle)gStrikeHand, true);
  65. }
  66.  
  67. //**********************************************************
  68. //
  69. // ShowScores - Poses alert which shows scores at game end
  70. //
  71. //**********************************************************
  72. void ShowScores(void)
  73. {
  74.     Str255    sYourScore, sOpponentScore;
  75.     long lYourScore, lOpponentScore;
  76.     
  77.     if (gLocalStruckCount+gOpponentStruckCount < 1)
  78.         return;    // don't bother if no faces struck
  79.  
  80.     lYourScore = gLocalStruckCount;
  81.     NumToString(lYourScore, sYourScore);
  82.     lOpponentScore = gOpponentStruckCount;
  83.     NumToString(lOpponentScore, sOpponentScore);
  84.     if (lYourScore > lOpponentScore)
  85.         ParamText("\pYou win!  Your Score: ", sYourScore, 
  86.             "\p, Opponent's Score: ", sOpponentScore);
  87.     else if (lYourScore < lOpponentScore)
  88.         ParamText("\pYou lose…  Your Score: ", sYourScore, 
  89.             "\p, Opponent's Score: ", sOpponentScore);
  90.     else
  91.         ParamText("\pIt's a tie…  Your Score: ", sYourScore, 
  92.             "\p, Opponent's Score: ", sOpponentScore);
  93.     Alert(rGenAlert, nil);
  94. }
  95.  
  96. //**********************************************************
  97. //
  98. // DrawSquare - Copies square's graphics to Rect for h, v
  99. //
  100. //**********************************************************
  101. static void DrawSquare(short h, short v)
  102. {
  103.     GrafPtr        oldPort;
  104.     Rect        destRect;
  105.     
  106.     if ((h >= 0) && (h < kNumCols) && (v >= 0) && (v < kNumRows)) {
  107.         GetPort(&oldPort);
  108.         SetPort(gGameWindow);
  109.         CopyBits((BitMap*)(*gPixMapHand[gSquare[h][v].occupant]),
  110.             &(gGameWindow->portBits), &gPicRect, 
  111.             &(gSquare[h][v].boundsRect), srcCopy, nil);
  112.         SetPort(oldPort);
  113.     }
  114. }
  115.  
  116. //**********************************************************
  117. //
  118. // NewGame - Sets up window and counters for new game, redraws
  119. //
  120. //**********************************************************
  121. void NewGame(void)
  122. {
  123.     short    h, v;
  124.     OSErr    theErr;
  125.  
  126.     gMadeCount = 0;
  127.     gLocalStruckCount = 0;
  128.     gOpponentStruckCount = 0;
  129.     
  130.     for (h = 0; h < kNumCols; h++)
  131.         for (v = 0; v < kNumRows; v++) {
  132.             gSquare[h][v].occupant = kEmpty;
  133.             DrawSquare(h,v);
  134.         }
  135. }
  136.  
  137. //**********************************************************
  138. //
  139. // MakeGWorld - Returns Ptr to a New GWorld used for graphics
  140. //
  141. //**********************************************************
  142. static GWorldPtr MakeGWorld(Rect *boundsPtr)
  143.     // borrowed GWorlds from MacTech 10, 5: Dave Mark
  144. {
  145.     GWorldPtr    newGWorld;
  146.     
  147.     if (NewGWorld(&newGWorld, 0, boundsPtr, nil, nil, noNewDevice))
  148.         return nil; // error occurred
  149.     else
  150.         return newGWorld;
  151. }
  152.  
  153. //**********************************************************
  154. //
  155. // NewGameWindow - Creates game's window and supporting stuff
  156. //
  157. //**********************************************************
  158. WindowPtr NewGameWindow(void)
  159. {
  160.     Rect        windRect, r;
  161.     short        h, v, i;
  162.     unsigned     long rs;
  163.     PicHandle    picHand;
  164.     GWorldPtr    picWorldPtr;
  165.     
  166.     gGameWindow = nil;
  167.     GetDateTime(&rs);        // create "unique random seed"
  168.     qd.randSeed = rs;
  169.     gStrikeHand = nil;        // load strike sound and channel
  170.     gSndChannelPtr = nil;
  171.     if (!SndNewChannel(&gSndChannelPtr, 0, initStereo, nil)) {
  172.         gStrikeHand = GetResource('snd ', rStrikeSound);
  173.         if (gStrikeHand) {
  174.             MoveHHi(gStrikeHand);
  175.             HLock(gStrikeHand);
  176.         }
  177.     }
  178.     SetRect(&windRect, kMargin,    // create game window
  179.         kMargin+kTitleBarHeight+LMGetMBarHeight(),
  180.         kMargin+kNumCols*kOffset+1, 
  181.         kMargin+kTitleBarHeight+LMGetMBarHeight()+kNumRows*kOffset+1);
  182.     gGameWindow = NewCWindow(nil, &windRect, "\pHave a nice day!",
  183.         false, rDocProc+2, (WindowPtr)-1, true, 0);
  184.     for (h = 0; h < kNumCols; h++)
  185.         for (v = 0; v < kNumRows; v++) { // set up "empty" game window
  186.             gSquare[h][v].occupant = kEmpty;
  187.             SetRect(&(gSquare[h][v].boundsRect), h*kOffset, v*kOffset,
  188.                 h*kOffset + kPieceWidth, v*kOffset +kPieceWidth);
  189.         }
  190.     SetRect(&gPicRect, 0, 0, kPieceWidth, kPieceWidth);
  191.     for (i = 0; i <= 3; i++) // Load graphics
  192.         if (picHand = GetPicture(i+rFirstGraphic)) {
  193.             picWorldPtr = MakeGWorld(&gPicRect);
  194.             gPixMapHand[i] = GetGWorldPixMap(picWorldPtr);
  195.             LockPixels(gPixMapHand[i]);
  196.             SetGWorld(picWorldPtr, nil);
  197.             HLock((Handle)picHand);
  198.             DrawPicture(picHand, &gPicRect);
  199.             HUnlock((Handle)picHand);
  200.             ReleaseResource((Handle)picHand);
  201.             picHand = nil;
  202.         } // Could abort if picture handle is not allocated 
  203.     ShowWindow(gGameWindow);
  204.  
  205.     return gGameWindow;
  206. }
  207.  
  208. //**********************************************************
  209. //
  210. // KillStruckFaces - Hunts down struck faces and kills them
  211. //
  212. //**********************************************************
  213. static void KillStruckFaces(void)
  214. {
  215.     short    h, v;
  216.  
  217.     for (h = 0; h < kNumCols; h++)
  218.         for (v = 0; v < kNumRows; v++)
  219.             if (gSquare[h][v].occupant > 1)
  220.                 if (TickCount() > (gSquare[h][v].age + kDyingTime)) {
  221.                     gSquare[h][v].occupant = kEmpty;
  222.                     gSquare[h][v].age = TickCount();
  223.                     DrawSquare(h,v);
  224.                     if (gSquare[h][v].madeLocally)
  225.                         --gMadeCount;
  226.                 }
  227. }
  228.  
  229. //*****************************************************************
  230. //
  231. // UpdateGameWindow - Draws any squares necessary for update event
  232. //
  233. //*****************************************************************
  234. void UpdateGameWindow(void)
  235. {
  236.     GrafPtr        oldPort;
  237.     Rect        UDRect;
  238.     RgnHandle    UDRgn;
  239.     short        h, v, v1, v2, h1, h2;
  240.     
  241.     GetPort(&oldPort);
  242.     SetPort(gGameWindow);
  243.     UDRgn = ((WindowPeek)gGameWindow)->updateRgn;
  244.     UDRect = (*UDRgn)->rgnBBox;
  245.     GlobalToLocal(&topLeft(UDRect));    // normalize
  246.     GlobalToLocal(&botRight(UDRect));
  247.     
  248.     v1 = UDRect.top/kOffset;    // convert to range of squares
  249.     v2 = UDRect.bottom/kOffset;
  250.     h1 = UDRect.left/kOffset;
  251.     h2 = UDRect.right/kOffset;
  252.     
  253.     BeginUpdate(gGameWindow);    // draw the squares in range
  254.         for (v = v1; v <= v2; v++)
  255.             for (h = h1; h <= h2; h++)
  256.                 DrawSquare(h, v);
  257.     EndUpdate(gGameWindow);
  258.     SetPort(oldPort);
  259. }
  260.  
  261. //**************************************************************
  262. //
  263. // ShowStruckFace - Draw struck face, play strike sound
  264. //                    occupant indicates local or opponent strike
  265. //
  266. //**************************************************************
  267. static void ShowStruckFace(short h, short v, short occupant)
  268. {
  269.     gSquare[h][v].occupant = occupant;
  270.     gSquare[h][v].age = TickCount();
  271.     DrawSquare(h,v);
  272.     PlayStrikeSound();
  273. }
  274.  
  275. //**********************************************************
  276. //
  277. // SendGameAction - Codify and send a message
  278. //
  279. //**********************************************************
  280. static OSErr SendGameAction(short action, short h, short v)
  281. {
  282.     unsigned char c;
  283.  
  284.     //Set to format AAHHHVVV AA = action, HHH = h, VVV = v
  285.     c = action;
  286.     c = c << 3;
  287.     c += h;
  288.     c = c << 3;
  289.     c += v;
  290.     
  291.     return putc(&c);
  292. }
  293.  
  294. //**********************************************************
  295. //
  296. // SendNewGame - Sends New Game action through connection
  297. //
  298. //**********************************************************
  299. OSErr SendNewGame(void)
  300. {
  301.     return SendGameAction(kActionNewGame, 0, 0);
  302. }
  303.  
  304. //*************************************************************
  305. //
  306. // HandleOpponentStrike - Respond to receipt of strike message
  307. //
  308. //*************************************************************
  309. static void HandleOpponentStrike(short h, short v, 
  310.     Boolean *inProgress)
  311. {
  312.     if (++gOpponentStruckCount >= kWinningScore)
  313.         *inProgress = false;
  314.     if (gSquare[h][v].occupant == kHealthy) {
  315.         ShowStruckFace(h, v, kStruckByOpponent); 
  316.         // only show strike if necessary, but always give credit 
  317.     }    // because other side validated face was there when struck
  318. }
  319.  
  320. //*************************************************************
  321. //
  322. // HandleWindowClick - Respond mouse down event on game window
  323. //
  324. //*************************************************************
  325. void HandleWindowClick(Point p, Boolean *inProgress)
  326. {
  327.     GrafPtr    oldPort;
  328.     
  329.     GetPort(&oldPort);
  330.     SetPort(gGameWindow);
  331.     GlobalToLocal(&p);
  332.     p.h /= kOffset;    // find struck square, protect against
  333.     p.v /= kOffset;    // invalid h or v; only strike healthy
  334.     if (p.h < kNumCols && p.v < kNumRows 
  335.             && gSquare[p.h][p.v].occupant == kHealthy) {
  336.         if (!SendGameAction(kActionStruckFace, p.h, p.v)) {
  337.             if (++gLocalStruckCount >= kWinningScore)
  338.                 *inProgress = false;// game ends at kWinningScore
  339.             ShowStruckFace(p.h, p.v, kStruckLocally);
  340.         }
  341.     }
  342.     SetPort(oldPort);
  343. }
  344.  
  345. //**********************************************************
  346. //
  347. // ShowFace - Show a newly-created healthy face at h, v
  348. //
  349. //**********************************************************
  350. static void ShowFace(short h, short v, Boolean local)
  351. {
  352.     gSquare[h][v].occupant = kHealthy;
  353.     gSquare[h][v].age = TickCount();
  354.     gSquare[h][v].madeLocally = local;
  355.     DrawSquare(h,v);
  356. }
  357.  
  358. //**********************************************************
  359. //
  360. // MakeFace - Find an empty square and put a face in it
  361. //
  362. //**********************************************************
  363. static void MakeFace(void)
  364. {
  365.     short    h, v;
  366.     long    maxH = kNumCols, maxV = kNumRows;
  367.  
  368.     do {
  369.         h =    ((unsigned short)Random() * maxH) / 65536;
  370.         v = ((unsigned short)Random() * maxV) / 65536;
  371.     } while (gSquare[h][v].occupant != kEmpty);
  372.     
  373.     if (SendGameAction(kActionMadeFace, h, v))
  374.         return;        // Maybe do something different if send fails
  375.     
  376.     ShowFace(h, v, true);
  377.     gMadeCount++;    // keep track of #faces made
  378. }
  379.  
  380. //**********************************************************
  381. //
  382. // HandleGameAction - Respond to a received game action
  383. //
  384. //**********************************************************
  385. void HandleGameAction(short action, short h, short v, 
  386.     Boolean *inProgress)
  387. {
  388.     if (action == kActionMadeFace)
  389.         ShowFace(h, v, false);
  390.     else if (action == kActionStruckFace)
  391.         HandleOpponentStrike(h, v, inProgress);
  392.     else { // action == kActionNewGame
  393.         NewGame();
  394.         *inProgress = true;
  395.     }
  396. }
  397.  
  398. //**********************************************************
  399. //
  400. // GetGameAction - Get message and if found decode & handle
  401. //
  402. //**********************************************************
  403. static OSErr GetGameAction(Boolean *inProgress)
  404. {
  405.     unsigned char     c;
  406.     OSErr            theErr;
  407.     short            action, h, v;
  408.     
  409.     theErr = getc(&c);
  410.     if (c && !theErr) {
  411.         action = c >> 6;
  412.         h = (c >> 3) & 0x7;
  413.         v = c & 0x7;
  414.         HandleGameAction(action, h, v, inProgress);
  415.     }
  416.     return theErr;
  417. }
  418.  
  419. //**********************************************************
  420. //
  421. // Look for game actions and maintain deaths and births
  422. //
  423. //**********************************************************
  424. void IdleGame(Boolean *inProgress)
  425. {
  426.     GetGameAction(inProgress); // should trap error returned
  427.     KillStruckFaces();
  428.     if (*inProgress)
  429.         if (gMadeCount < kMaxMade)
  430.             MakeFace();
  431. }
  432.